module c_func
    use iso_c_binding
    implicit none
    interface
        subroutine close_buffer()bind(c,name="close_buffer")
        end subroutine close_buffer
        function getch()bind(c,name="getch_c")result(f1)
            use iso_c_binding
            character(kind=c_char,len=1)::f1
        end function getch
    end interface
    integer,parameter::n=4
    integer,parameter::win=2048
    integer::a(n,n)
contains
    function random_two()result(f1)
        real::r(2)
        integer::f1(2)
        call random_number(r)
        f1=ceiling(r*n)
    end function

    subroutine init()
        real::r
        integer::idx(2)
        a=0
        call random_number(r)
        idx=random_two()
        a(idx(1),idx(2))=int(r+1.5)*2
    end subroutine

    subroutine move(str)
        character(len=1)::str
        integer::i
        integer::x(n)
        real::r
        integer::idx(2)
        integer::tmp(n,n)
        tmp=a
        select case(str)
        case("w")
            do i=1,n
                x=a(:,i)
                call add(x)
                a(:,i)=x(:)
            end do
        case("a")
            do i=1,n
                x=a(i,:)
                call add(x)
                a(i,:)=x(:)
            end do
        case("s")
            do i=1,n
                x=a(n:1:-1,i)
                call add(x)
                a(:,i)=x(n:1:-1)
            end do
        case("d")
            do i=1,n
                x=a(i,n:1:-1)
                call add(x)
                a(i,:)=x(n:1:-1)
            end do
        case default
        end select
        if(any(a==win))then
            write(*,*)"YOU WIN"
            stop
        end if
        if(all(a==tmp))then
            if(isgameover())then
                write(*,*)"GAME OVER"
                stop
            else
                return
            end if
        end if
        call random_number(r)
        do
            idx=random_two()
            if(a(idx(1),idx(2))==0)then
                a(idx(1),idx(2))=int(r+1.5)*2
                exit
            end if
        end do
    end subroutine move

    function isgameover() result(f1)
        logical::f1
        integer::i,j
        do i=1,n
            do j=1,n-1
                if(a(i,j)==a(i,j+1))then
                    f1=.false.
                    return
                end if
            end do
            do j=1,n-1
                if(a(j,i)==a(j+1,i))then
                    f1=.false.
                    return
                end if
            end do
            f1=.true.
        end do
    end function isgameover

    subroutine write_matrix()
        integer::i
        call execute_command_line("clear")
        write(*,"(A20,A)")repeat(" ", 20),repeat("----", n+1)
        do i=1,n
            write(*,"(A20,'|',*(I4,'|'))")repeat(" ", 20),a(i,:)
            write(*,"(A20,A)")repeat(" ", 20),repeat("----", n+1)
        end do
    end subroutine write_matrix

    subroutine add(x)
        integer,intent(inout)::x(n)
        integer::i
        x=pack(x,x/=0,[(0,i=1,n)])
        do i=1,n-1
            if(x(i)==x(i+1))then
                x(i)=2*x(i)
                x(i+1)=0
            end if
        end do
        x=pack(x,x/=0,[(0,i=1,n)])
    end subroutine add
end module c_func

program main
    use c_func
    implicit none
    character(len=1)::str
    call close_buffer()
    call init()
    call write_matrix()
    do
        str=getch()
        call move(str)
        call write_matrix()
    end do
end program main
